//============================================================================================
/**
 * @file	pair_poke_fld_move.c
 * @brief	連れ歩きフィールド動作イベント（秘伝技とか）
 * @author	Nozomu Saito
 * @date	2007.06.25
 */
//============================================================================================
#include "pair_poke_fld_move.h"
#include "field_event.h"
#include "pair_poke.h"

typedef struct POSITION_tag
{
	int PosX;
	int PosZ;
}POSITION;

typedef struct P_POKE_FLD_MOVE_WORK_tag
{
	int Seq;				//シーケンス
	FIELD_OBJ_PTR TargetA;	//対象ＡのフィールドＯＢＪ
	FIELD_OBJ_PTR TargetB;	//対象ＢのフィールドＯＢＪ
	POSITION TargetA_Pos;	//対象Ａの位置
	int TargetA_Dir;		//対象Ａの向き
	POSITION TargetB_Pos;	//対象Ｂの位置
}P_POKE_FLD_MOVE_WORK;

static P_POKE_FLD_MOVE_WORK *AllocWork(FIELD_OBJ_PTR inTargetA, FIELD_OBJ_PTR inTargetB);
static BOOL ChangePositionEvent(GMEVENT_CONTROL * event);

//アニメコード関連
static int DecideMoveDirAnmCode(const POSITION *inTarget1, const POSITION *inTarget2);
static int GetDirAnmCode(const u8 inDir);
static int GetReverseDirAnmCode(const u8 inDir);
static int GetReverseMoveAnmCode(const int inAnmCode);

//==============================================================================
/**
 * イベントコール
 *
 * @param	inTargetA		対象フィールドOBJ
 * @param	inTargetB		対象フィールドOBJ
 * @apram	parent_event	親イベント
 *
 * @retval	none
 */
//==============================================================================
void PairPokeFldMove_CallEvent(FIELD_OBJ_PTR inTargetA, FIELD_OBJ_PTR inTargetB,
								GMEVENT_CONTROL *parent_event)
{
	P_POKE_FLD_MOVE_WORK *work = AllocWork(inTargetA,inTargetB);
	FieldEvent_Call(parent_event, ChangePositionEvent, work);
}

//==============================================================================
/**
 * イベント用メモリアロケーション
 *
 * @param	inTargetA		対象フィールドOBJ
 * @param	inTargetB		対象フィールドOBJ
 *
 * @retval  P_POKE_FLD_MOVE_WORK_PTR	イベントワークポインタ
 */
//==============================================================================
static P_POKE_FLD_MOVE_WORK *AllocWork(FIELD_OBJ_PTR inTargetA, FIELD_OBJ_PTR inTargetB)
{
	P_POKE_FLD_MOVE_WORK *work;
	work = sys_AllocMemoryLo( HEAPID_FIELD, sizeof(P_POKE_FLD_MOVE_WORK) );
	work->Seq = 0;
	work->TargetA = inTargetA;
	work->TargetB = inTargetB;
	return work;
}

//==============================================================================
/**
 * 自機と連れ歩きポケモンの位置を入れ替える
 *
 * @param	work	イベントワークポインタ
 *
 * @retval  BOOL	TRUE:イベント終了　FALSE：イベント継続
 */
//==============================================================================
static BOOL ChangePositionEvent(GMEVENT_CONTROL * event)
{
	FIELDSYS_WORK * fsys = FieldEvent_GetFieldSysWork(event);
	P_POKE_FLD_MOVE_WORK *work = FieldEvent_GetSpecialWork(event);
	BOOL rc = FALSE;
	switch (work->Seq){
	case 0:	//連れ歩きポーズ解除
		FieldOBJ_MovePauseClear( PairPoke_GetFldObjPtr(fsys) );
		work->Seq++;
		/* FALL THROUGH */
	case 1:
		//対象Ａ、Ｂのアニメ終了を待つ
		if( FieldOBJ_AcmdSetCheck(work->TargetA) && FieldOBJ_AcmdSetCheck(work->TargetB) ){
			//連れ歩きポーズ
			FieldOBJ_MovePause( PairPoke_GetFldObjPtr(fsys) );
			work->Seq++;
		}
		break;
	case 2:
		//対象Ａの位置と向きを保存
		work->TargetA_Pos.PosX = FieldOBJ_NowPosGX_Get(work->TargetA);
		work->TargetA_Pos.PosZ = FieldOBJ_NowPosGZ_Get(work->TargetA);
		work->TargetA_Dir = FieldOBJ_DirDispGet(work->TargetA);
		//対象Ｂの位置を保存
		work->TargetB_Pos.PosX = FieldOBJ_NowPosGX_Get(work->TargetB);
		work->TargetB_Pos.PosZ = FieldOBJ_NowPosGZ_Get(work->TargetB);
		//ＡとＢの入れ替え
		{
			int anm_code;
			//保存したＢの位置と現在のＡ位置を比較して進む方向を決定し、ＡをＢの場所へ動かす（アニメコマンド発行）
			anm_code = DecideMoveDirAnmCode(&work->TargetA_Pos, &work->TargetB_Pos);
			FieldOBJ_AcmdSet(work->TargetA, anm_code);	//アニメセット
			//ＢをＡの場所へ動かす（アニメコマンド発行）
			anm_code = GetReverseMoveAnmCode(anm_code);
			FieldOBJ_AcmdSet(work->TargetB, anm_code);	//アニメセット
		}
		work->Seq++;
		break;
	case 3:
		//Ａ、Ｂアニメ終了待ち
		if( FieldOBJ_AcmdSetCheck(work->TargetA) && FieldOBJ_AcmdSetCheck(work->TargetB) ){
			work->Seq++;
		}
		break;
	case 4:
		{
			int anm_code;
			u8 dir;
			//Ａの向きを逆向き(Ｂのいる方)にする（アニメコマンド発行）
			dir = FieldOBJ_DirDispGet(work->TargetA);
			anm_code = GetReverseDirAnmCode(dir);
			FieldOBJ_AcmdSet(work->TargetA, anm_code);	//アニメセット
			//保存したＡ方向にＢを向かせる（アニメコマンド発行）
			anm_code = GetDirAnmCode(work->TargetA_Dir);
			FieldOBJ_AcmdSet(work->TargetB, anm_code);	//アニメセット
		}
		work->Seq++;
		break;
	case 5:
		if( FieldOBJ_AcmdSetCheck(work->TargetA) && FieldOBJ_AcmdSetCheck(work->TargetB) ){
			//アニメがすべて終わったので、終了コマンドをセット
			FieldOBJ_AcmdEnd(work->TargetA); //アニメ終了
			FieldOBJ_AcmdEnd(work->TargetB); //アニメ終了
			//終了
			rc = TRUE;
			//ワーク解放
			sys_FreeMemoryEz(work);
		}
		break;
	}

	return rc;
}

//------------------------------------------------------------------------------
//アニメコード関連

//==============================================================================
/**
 * 移動方向の決定
 *
 * @param	inTarget1	対象1の位置
 * @param	inTarget2	対象2の位置
 *
 * @retval  move_dir	移動コマンド
 */
//==============================================================================
static int DecideMoveDirAnmCode(const POSITION *inTarget1, const POSITION *inTarget2)
{
	int move_dir = AC_WALK_D_8F;//下向きをデフォルトにしておく
	//
	if (inTarget1->PosX == inTarget2->PosX){	//Ｘ座標が同じ（2体が縦に配置）
		//移動方向を決定する
		if (inTarget1->PosZ > inTarget2->PosZ){
			//上に移動
			move_dir = AC_WALK_U_8F;
		}else if (inTarget1->PosZ < inTarget2->PosZ){
			//下に移動
			move_dir = AC_WALK_D_8F;
		}else{			//座標が同一
			GF_ASSERT(0);
		}
	}else if(inTarget1->PosZ == inTarget2->PosZ){	//Z座標が同じ（2体が横に配置）
		//移動方向を決定する
		if (inTarget1->PosX > inTarget2->PosX){
			//左に移動
			move_dir = AC_WALK_L_8F;
		}else if (inTarget1->PosX < inTarget2->PosX){
			//右に移動
			move_dir = AC_WALK_R_8F;
		}else{			//座標が同一
			GF_ASSERT(0);
		}
	}else{	//同一直線上にいない
		GF_ASSERT(0);
	}

	return move_dir;
}

//==============================================================================
/**
 * 指定方向を向くアニメコードを返す
 *
 * @param	inDir		方向
 *
 * @retval  dir			方向アニメコード
 */
//==============================================================================
static int GetDirAnmCode(const u8 inDir)
{
	switch(inDir){
	case DIR_UP:
		return AC_DIR_U;
	case DIR_DOWN:
		return AC_DIR_D;
	case DIR_LEFT:
		return AC_DIR_L;
	case DIR_RIGHT:
		return AC_DIR_R;
	}

	GF_ASSERT(0);
	return 0;
}

//==============================================================================
/**
 * 指定方向の逆を向くアニメコードを返す
 *
 * @param	inDir		方向
 *
 * @retval  dir			方向アニメコード
 */
//==============================================================================
static int GetReverseDirAnmCode(const u8 inDir)
{
	switch(inDir){
	case DIR_UP:
		return AC_DIR_D;
	case DIR_DOWN:
		return AC_DIR_U;
	case DIR_LEFT:
		return AC_DIR_R;
	case DIR_RIGHT:
		return AC_DIR_L;
	}

	GF_ASSERT(0);
	return 0;
}

//==============================================================================
/**
 * 指定移動アニメコードと逆の向きに移動するアニメコードを返す
 *
 * @param	inAnmCode	移動アニメコード
 *
 * @retval  move_dir	移動アニメコード
 */
//==============================================================================
static int GetReverseMoveAnmCode(const int inAnmCode)
{
	switch(inAnmCode){
	case AC_WALK_U_8F:
		return AC_WALK_D_8F;
	case AC_WALK_D_8F:
		return AC_WALK_U_8F;
	case AC_WALK_L_8F:
		return AC_WALK_R_8F;
	case AC_WALK_R_8F:
		return AC_WALK_L_8F;
	}

	GF_ASSERT(0);
	return 0;
}